Connecting custom functions to the report

FastReport has a quite large number of standard functions, which can be used in a report. There also is a possibility to connect your own functions. Connection of functions is performed via the “FastScript” script library's interface, which is included in FastReport (to know more about FastScript, refer to the manual of this library).

Let us examine an example of how a procedure and/or a function can be connected. There are two basic ways to perform it: either by using “FastScript” interface, or with help of the “TfrxReport” component's methods. Quantity and type of the connected function's parameters can be different. One cannot transfer parameters of the “Set” and “Record" type, as they are not supported in FastScript. It is required to transfer such parameters as simpler types, for example, to transfer the TRect as X0, Y0, X1, Y1: Integer. See more about process of adding functions with different parameters in the FastScript documentation.

Way 1:

uses fs_iinterpreter;

function TForm1.MyFunc(s: String ; i: Integer): Boolean;

begin

// necessary logic

end;

procedure TForm1.MyProc(s: String );

begin

// necessary logic

end;

function TForm1.CallMethod(Instance: TObject; ClassType: TClass; const MethodName: String ;

var Params: Variant): Variant;

begin

if MethodName = 'MYFUNC' then

Result := MyFunc(Params[0], Params[1])

else if MethodName = 'MYPROC' then

MyProc(Params[0]);

end;

fsGlobalUnit.AddMethod('function MyFunc(s: String; i: Integer): Boolean', CallMethod);

fsGlobalUnit.AddMethod('procedure MyProc(s: String)', CallMethod);

First of all, you should add functions' descriptions via calling fsGlobalUnit.AddMethod. The first parameter is the syntax's description; the second one is a link to the function's handler. Next step would be creation of a handler of the “TfsCallMethodEvent” type and realization of functions' call in it. The handler is the function of a class:

TfsCallMethodEvent = function (Instance: TObject; ClassType: TClass;

const MethodName: String ; var Params: Variant): Variant of object ;

We do not need the “Instance,” and “ClassType” parameters yet. “MethodName” is the name of a function in the upper case; Params is the array of parameters.

Way 2:

function TForm1.MyFunc(s: String ; i: Integer): Boolean;

begin

// necessary logic

end;

procedure TForm1.MyProc(s: String );

begin

// necessary logic

end;

function TForm1.frxReport1UserFunction( const MethodName: String ;

var Params: Variant): Variant;

begin

if MethodName = 'MYFUNC' then

Result := MyFunc(Params[0], Params[1])

else if MethodName = 'MYPROC' then

MyProc(Params[0]);

end;

frxReport1.AddFunction('function MyFunc(s: String; i: Integer): Boolean');

frxReport1.AddFunction('procedure MyProc(s: String)');

This way is a little easier. Functions' descriptions are now added via the “TfrxReport.AddFunction” method with a single parameter. Functions' call is realized in the TfrxReport.OnUserFunction event's handler.

Both ways of connection are equivalent. The connected function can be used in the report's script; furthermore, one can refer to it from the objects of the “TfrxMemoView” type. The function is also displayed in the "Data tree" window. In this window functions are split into categories, and thus when you select any function, the hint about this function appears at the bottom of the window.

Let us modify the code of our examples to register functions in a separate category, and display the function's description:

the first way:

fsGlobalUnit.AddMethod('function MyFunc(s: String; i: Integer): Boolean', CallMethod, 'My functions', 'The MyFunc function always returns True');

fsGlobalUnit.AddMethod('procedure MyProc(s: String)', CallMethod, 'My functions', 'The MyProc procedure does not do anything');

the second way:

frxReport1.AddFunction('function MyFunc(s: String; i: Integer): Boolean', 'My functions', 'The MyFunc function always returns True');

frxReport1.AddFunction('procedure MyProc(s: String)', 'My functions', 'The MyProc procedure does not do anything');

If you want to register functions in one of the standard categories, use the following categories' names:

'ctString' – string function;

'ctDate' - date/time functions;

'ctConv' - conversion functions;

'ctFormat' - formatting;

'ctMath' - mathematical functions;

'ctOther' - other functions.

If a blank category's name is specified, the function is placed to the root of the functions' tree.

If you are going to connect a large number of functions, it is recommended to carry out all the logic into a separate unit. Here is an example of such unit:

unit myfunctions;

interface

implementation

uses SysUtils, Classes, fs_iinterpreter;

type

TFunctions = class (TObject)

private

function CallMethod(Instance: TObject; ClassType: TClass; const

MethodName: String ; var Params: Variant): Variant;

public

constructor Create;

destructor Destroy; override ;

end;

var

Functions: TFunctions;

function MyFunc(s: String ; i: Integer): Boolean;

begin

// necessary logic

end;

procedure MyProc(s: String );

begin

// necessary logic

end ;

{ TFunctions }

constructor TFunctions.Create;

begin

with fsGlobalUnit do

begin

AddedBy := Self;

AddMethod('function MyFunc(s: String; i: Integer): Boolean',

CallMethod, 'My functions', 'The MyFunc function always returns True');

AddMethod('procedure MyProc(s: String)', CallMethod, 'My functions',

'The MyProc procedure does not do anything'');

AddedBy := nil ;

end;

end;

destructor TFunctions.Destroy;

begin

if fsGlobalUnit <> nil then

fsGlobalUnit.RemoveItems(Self);

inherited;

end;

function TFunctions.CallMethod(Instance: TObject; ClassType: TClass;

const MethodName: String ; var Params: Variant): Variant;

begin

if MethodName = 'MYFUNC' then

Result := MyFunc(Params[0], Params[1])

else if MethodName = 'MYPROC' then

MyProc(Params[0]);

end;

initialization

Functions := TFunctions.Create;

finalization

Functions.Free;

end.